package com.guli.mall.serviceBase.config;
/*
 * @Author 罗
 * @date 2020/8/27 - 8:51 下午
 */

import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.*;

import java.net.UnknownHostException;

@ConditionalOnClass({RedisOperations.class, CacheProperties.class})
@EnableConfigurationProperties(CacheProperties.class)
@Slf4j
public class RedisCacheConfigurationConfig {


    /**
     * 注意，这个方式并不能序列化 RedisTemplate 或者 StringRedisTemplate
     * 因为，RedisTemplate 是直接通过 RedisConnectionFactory 来创建的，没有用到 RedisCacheConfiguration 对象
     *
     * @param cacheProperties
     * @return
     * @ConditionalOnClass(CacheManager.class)
     * @ConditionalOnBean(CacheAspectSupport.class)
     * @ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
     * @EnableConfigurationProperties(CacheProperties.class) 已经帮我们创建了 CacheProperties，所以可以直接拿来用
     * public class CacheAutoConfiguration {
     */

    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {

        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();

        /**
         * 只能在已有的配置基础上修改，修改之后返回的是新的对象，需要将原有的对象覆盖（源码中也是这么配置的）
         * 后面还会从 yaml 中读取配置，就将这里注释了
         * */
        /*Duration duration = Duration.ofSeconds(60);
        config = config.entryTtl(duration);*/

        /**
         * 配置key的序列化
         * */
        RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer();

        RedisSerializationContext.SerializationPair<String> serializationPairForKey = RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer);

        config = config.serializeKeysWith(serializationPairForKey);

        /**
         * 配置value的序列化
         * */
/*
        不建议使用，因为要为每一个序列化对象都指定一个序列化方式
        // 使用jackson作为value的序列化器 但是只支持一种类型，这里只支持 CategoryEntity 类

        RedisSerializer<CategoryEntity> fastJsonRedisSerializer = new Jackson2JsonRedisSerializer<>(CategoryEntity.class);

        RedisSerializationContext.SerializationPair<CategoryEntity> serializationPairForValueByJackson = RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer);
*/

        // 使用 范型的序列化器可以支持所有对象的序列化 (阿里的fast实现)，原理是在json中存放一份序列化之前，对象的全限定的路径
        RedisSerializer<Object> genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();

        RedisSerializationContext.SerializationPair<Object> serializationPair = RedisSerializationContext.SerializationPair.fromSerializer(genericFastJsonRedisSerializer);

        // 支持范型的序列化器 （jackson实现），jackson序列化时，调用get() set()方法来进行序列化，无论该属性是否有值，都一并调用 set() get() 进行序列化与反序列化
        RedisSerializer<Object> genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();

        RedisSerializationContext.SerializationPair<Object> serializationPairForGenericValueByJackson = RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer);

        config = config.serializeValuesWith(serializationPair);

        /**
         * 读取 ymal 文件中的配置，以 spring.cache 开头
         * */
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();

        // 以 spring.cache.redis 开头
        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixKeysWith(redisProperties.getKeyPrefix());
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }

        log.info("redis配置完成。。");

        return config;
    }


    //@ConditionalOnBean(RedisConnectionFactory.class)
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        RedisSerializer<Object> genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();

        template.setDefaultSerializer(genericFastJsonRedisSerializer);

        log.info("RedisTemplate json 序列化设置完成");
        return template;
    }

    //@ConditionalOnBean(RedisConnectionFactory.class)
    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);

        RedisSerializer<Object> genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();

        template.setDefaultSerializer(genericFastJsonRedisSerializer);

        log.info("StringRedisTemplate json 序列化设置完成");

        return template;
    }
}
